'''
Created on 2010-1-26

@author: fangtian
'''

import os
import urllib.request
import urllib.parse
import re
import sqlite3
import time
import copy
import http
import datetime

import flickrapi 

class CmdFramework:
    def __SampleCommandHandler(self, argVector):
        print('This is a sample command handler!!')
    
    def __SampleHelp(self):
        print('This is a sample help!!!')
        
    def __init__(self):
        self.__commandDict = dict()
        self.__commandDict['-SAMPLE'] = [self.__SampleCommandHandler, self.__SampleHelp, 'Sample short descriptions']
    
    def __PrintGeneralHelp(self):
        (folder, file) = os.path.split(os.sys.argv[0])
        print('=================General Help=====================')
        print('Help: ' + file + ' <command> [command argument]')
        print('===================Commands=======================')
        index = 1
        for cmdFlag in self.__commandDict.keys():
            print(str(index) + '. ' + cmdFlag + ':\t' + self.__commandDict[cmdFlag][2])
            index += 1
            
            
    def AddCommand(self, commandStr, commandHandler, commandHelp, shortHelpString):
        self.__commandDict[commandStr.upper()] = [commandHandler, commandHelp, shortHelpString]
            
    def Exec(self, osArgV):
        if (len(osArgV) == 1):
            self.__PrintGeneralHelp()
            return
        else:
            commandFlag = osArgV[1].upper();
            if (commandFlag in self.__commandDict) == True:
                argVector = []
                if (len(osArgV) > 1):
                    argVector = osArgV[2:]
                    
                if (self.__commandDict[commandFlag][0](argVector) == True):
                    return
                else:
                    self.__commandDict[commandFlag][1]()
            else:
                self.__PrintGeneralHelp()
                return

def StripEmptyAtBothEnd(instr):
    return instr.strip()

def TruncateComment(instr, reg):
    if (isinstance(instr, type('')) == False):
        raise TypeError('TruncateComment() only accepts string as input!!!')
    
    res = reg.sub('', instr)
    res = StripEmptyAtBothEnd(res)  
    return res

def BuildCommentReg():
    return re.compile('((?P<comment>//[\w\W]*)$)')

def BuildAssignmentReg():
    return re.compile('(\$(?P<varName>[\w]+)\$[\s]*=[\s]*(?P<varValue>[\w\W]*))?(//(?P<comment>[\w\s]*))?')

def GetAssignment(instr, reg):
    varName = None
    varValue = None
    res = True
    mat = reg.match(instr)
    if mat:
        varName = mat.group('varName')
        varValue = mat.group('varValue')
    else:
        res = False
    return res, varName, varValue

def ReadSetting(settingPath, default = None):
    setting = default
    commentReg = BuildCommentReg()
    assignReg = BuildAssignmentReg()
    lineNum = 0
    try:
        with open(settingPath, 'rb') as settingFile:
            for line in settingFile:
                line = bytes.decode(line)
                line = str(StripEmptyAtBothEnd(line))
                line = TruncateComment(line, commentReg)
                lineNum += 1
                if (len(line) <= 0):
                    continue
                (res, varName, varValue) = GetAssignment(line, assignReg)
                if (res):
                    setting[varName] = varValue
    except:
        print('Exception is raised when parsing the setting file at line '\
               + str(lineNum) + ' in file ' + settingPath)
        pass
                
    # a little clean up
    if ('download_interval' in setting):
        setting['download_interval'] = float(setting['download_interval'])
    if ('max_download_num' in setting):
        setting['max_download_num'] = int(setting['max_download_num'])
    if ('all_download_num' in setting):
        setting['all_download_num'] = int(setting['all_download_num'])
    if ('new_download_num' in setting):
        setting['new_download_num'] = int(setting['new_download_num'])
    if ('db_commit_size' in setting):
        setting['db_commit_size'] = int(setting['db_commit_size'])
    return setting





def SetupDatabase(dbPath):
    '''
    The database contains the folllowing table
    1. photos
    id | owner | size | secret | tags | title
    id is the photo id encoded in integer
    owner is the id of the owner of the photo enocded in integer.
         To gee nsid, simply split the integer into the two parts
         as <1stPart|2ndPart> where 2nd part is the last two digits and
         recover the nsid as 1stPart@N2ndPart
    size is a text showing the size of downloaded file.
        'o' for original size 'o'
        'o' for large size 'o'
        '' for medium size ''
        'm' for small size 'm'
        't' for thumbnail 't'
        's' for square 's'
        null for not yet downloaded
    secret is a secret string for the image file.
        Together with the photo id. and size, the file name is
        id_secret_size.jpg or id_secret.jpg if size is ''      
    '''
    connection = None
    try:
        connection = sqlite3.connect(dbPath)
        print('Connected to database ' + dbPath)
        
        cur = connection.cursor()
        
        cur.execute(''' select tbl_name from sqlite_master ''')
        
        list_tables = cur.fetchall()
        
        isTablePhotosExists = False
        isTableExifExists = False
        for table in list_tables:
            if ('photos' == table[0]):
                isTablePhotosExists = True
            if ('exifs' == table[0]):
                isTableExifExists = True
            
        if (isTablePhotosExists ==  False):
            cur.execute('''create table if not exists photos 
                    (id integer primary key asc not null
                     , owner integer, size text
                     , secret text, tags text, tile text)''')
            print('Creating table photos')
            connection.commit()
        else:
            print('Table photos exists')
        
        if (isTableExifExists ==  False):
            cur.execute('''create table if not exists exifs 
                    (id integer primary key asc not null
                     , sizew integer, sizeh integer
                     , ccdw integer, ccdh integer, focal FLOAT, cam TEXT, make TEXT)''')
            print('Creating table exifs')
            connection.commit()
        else:
            print('Table exifs exists')
        
        cur.close()
        # setup table for image insertion
        # setup table for user information
        # setup table for  
    except:
        print('Error when establishing the connection with database ' + dbPath)
        return None
    return connection

def flickr_retry_call(flickr, warp_method, retries = 1000, retriesInterval = 60, *argv, **argdict):
    result = None
    tried = 0
    while (isinstance(result, type(None)) == True and tried < retries):
        try:
            flickr_warp_method = getattr(flickr, warp_method)
            result = flickr_warp_method(*argv, **argdict)
            pass
        except Exception as e:
            # cleanup code if exception happen
            isRetry = False
            if (isinstance(e, flickrapi.exceptions.FlickrError) == True):
                print(e)
                errorMsgComp = str(e).split(':')
                if (StripEmptyAtBothEnd(errorMsgComp[2]) == 'Sorry, the Flickr API service is not currently available....'
                    or int(StripEmptyAtBothEnd(errorMsgComp[1])) == 0):
                    isRetry = True
                    
            if (isinstance(e, urllib.error.URLError) == True):
                print(str(e))
                isRetry = True
            
            if (isinstance(e, http.client.HTTPException) == True):
                print(str(e))
                isRetry = True
           
            if (isRetry == True):
                print('\n-------------------------------')
                print('Wait ', str(retriesInterval),' seconds before retry!!!')
                print('-------------------------------\n')
                time.sleep(float(retriesInterval))
                tried += 1
                result = None
            else:
                print(str(e), ' is caught!!!')
                raise e

    return result

def PotentialQueryResult(flickr, querySetting):
    rsp = flickr_retry_call(flickr, 'photos_search', **querySetting)
    photoset = rsp.getchildren()[0]
    totalPageInOneQuery = int(photoset.get('pages'))
    totalPhotoPerPage = int(photoset.get('perpage'))
    
    print('The query returns ' + str(totalPageInOneQuery*totalPhotoPerPage) + ' results.')
    print('The query returns ' + str(totalPageInOneQuery) + ' pages.')
    return totalPageInOneQuery*totalPhotoPerPage

def flickr_retry_walk(flickr, retries = 1000, maximumDownload = 1000, maxQueryResult = 3500, **params):
    
    if ('min_upload_date' not in params):
        params['min_upload_date'] = '0'
    if ('max_upload_date' not in params):
        params['max_upload_date'] = str(int(time.time()))
    potentialQueryResultNum = PotentialQueryResult(flickr, params)
    
    nextParams = copy.deepcopy(params)
    currentParams = None
    
    
    while isinstance(nextParams, type(None)) == False :
        
        if potentialQueryResultNum >= maxQueryResult and maximumDownload >= maxQueryResult:
            print('\n===============================================')
            print('Too many results!!!')
            print(datetime.date.fromtimestamp(float(nextParams['min_upload_date'])).strftime("%Y-%m-%d %H:%M:%S"),
                   '-',
                    datetime.date.fromtimestamp(float(nextParams['max_upload_date'])).strftime("%Y-%m-%d %H:%M:%S"))
            print('The query is partitioned into smaller parts!!!')
            print('===============================================\n')
            
            currentParams = copy.deepcopy(nextParams)
            
            print('Iteratively finding the time slice that the number of query results are smaller than ', str(maxQueryResult))
            while potentialQueryResultNum > maxQueryResult:
                mediumDate = (float(currentParams['max_upload_date']) + float(currentParams['min_upload_date']))/2
                
                if (int(currentParams['max_upload_date']) - mediumDate) > 24*60*60*7:
                    mediumDate = int(currentParams['max_upload_date']) - 24*60*60*7
        
                currentParams['min_upload_date'] = str(mediumDate)
                nextParams['max_upload_date'] = str(mediumDate)
                
                print(datetime.date.fromtimestamp(float(nextParams['min_upload_date'])).strftime("%Y-%m-%d %H:%M:%S"),
                       '-',
                        datetime.date.fromtimestamp(float(nextParams['max_upload_date'])).strftime("%Y-%m-%d %H:%M:%S"))
                print(datetime.date.fromtimestamp(float(currentParams['min_upload_date'])).strftime("%Y-%m-%d %H:%M:%S"),
                       '-',
                        datetime.date.fromtimestamp(float(currentParams['max_upload_date'])).strftime("%Y-%m-%d %H:%M:%S"))
                potentialQueryResultNum = PotentialQueryResult(flickr, currentParams)
        else:
            currentParams = copy.deepcopy(nextParams)
            nextParams = None
        
        page = 1
        total = 1 # We don't know that yet, update when needed
        while page <= total:
            # Fetch a single page of photos
            rsp = flickr_retry_call(flickr, 'photos_search', retries = retries, page=page, **currentParams)
    
            photoset = rsp.getchildren()[0]
            total = int(photoset.get('pages'))
    
            photos = rsp.findall('*/photo')
    
            # Yield each photo
            for photo in photos:
                yield photo
    
            # Ready to get the next page
            page += 1
        
        if isinstance(nextParams, type(None)) == False:
            print(datetime.date.fromtimestamp(float(nextParams['min_upload_date'])).strftime("%Y-%m-%d %H:%M:%S"),
               '-',
                datetime.date.fromtimestamp(float(nextParams['max_upload_date'])).strftime("%Y-%m-%d %H:%M:%S"))
            potentialQueryResultNum = PotentialQueryResult(flickr, nextParams)
            

def flickr_getRecent(flickr, maxNum=10000):
    if (isinstance(flickr, flickrapi.FlickrAPI) == False):
        return None
    
    recentPhotos = list()
    recent = flickr.photos_getRecent()
    print(recent.find('photos').get('pages'))
    totalPage = int(recent.find('photos').get('pages'))
    for pageIndex in range(1, totalPage+1):
        recent = flickr_retry_call(flickr, 'photos_getRecent', page=str(pageIndex))
#        recent = flickr.photos_getRecent(page=str(pageIndex))
        photos = recent.findall('photos/photo')
        recentPhotos.extend(photos)
        if (len(recentPhotos) > maxNum):
            break
    
    print('Got ' + str(len(recentPhotos)) + ' recent photos!!!')
    return recentPhotos

def DownloaderHandler(argVector):
    if (len(argVector) < 1):
        print('Incorrect parameters')
        return False
    
    settingPath = argVector[0]
        
    setting = ReadSetting(settingPath, dict())
    
    dbConnection = SetupDatabase(setting['db_file'])
       
    if (isinstance(dbConnection, type(None))):
        return False
    
    dbCur = dbConnection.cursor();
    
    if (os.path.exists(setting['image_folder']) == False):
        os.mkdir(setting['image_folder'])
        print('Creating folder ' + setting['image_folder'])
        
    querySetting = dict()
    if ('query_setting' in setting):
        querySettingPath = setting['query_setting']
        querySetting = ReadSetting(querySettingPath, dict())
        
    flickr = flickrapi.FlickrAPI(setting['api_key'])
    

    # please refer http://www.flickr.com/services/api/flickr.photos.search.html
    # for more detailed parameters
    
    if (querySetting['get_recent'] == 'false'):
        if (len(argVector) > 1):
            keyWords = argVector[1:]
            tagStr = ','.join(keyWords)
            querySetting['tags'] = tagStr

        PotentialQueryResult(flickr, querySetting)
        
        maxNum = 0
        if (setting['new_download_num'] < setting['all_download_num']):
            maxNum = setting['all_download_num']
        else:
            maxNum = setting['new_download_num']
            
        photos = flickr_retry_walk(flickr, maximumDownload = maxNum, **querySetting)
    else:
        maxNum = 0
        if (setting['new_download_num'] < setting['all_download_num']):
            maxNum = setting['all_download_num']
        else:
            maxNum = setting['new_download_num']
        photos = flickr_getRecent(flickr, maxNum)
   
    allDownloadedNum = 0
    newDownloadedNum = 0
    sizeTable = {'Square':0
                 , 'Thumbnail':1
                 , 'Small':2
                 , 'Medium':3 
                 , 'Large':4
                 , 'Original':5}
       
   
    for photo in photos:
        try:
            if (newDownloadedNum >= setting['new_download_num'] 
                and setting['new_download_num'] > 0):
                break
            if (allDownloadedNum >= setting['all_download_num']
                and setting['all_download_num'] > 0):
                break
            
            print('')
            try:
                print(photo.get('title') + ' ~~ ' + photo.get('id'))
            except:
                print('Error in printing information, but the process is NOT harmed!!! Easy!')
            
            
            # check whether this photo has been downloaded
            sqlCmd = '''select id from photos where id = ''' + str(photo.get('id'))
            
            dbCur.execute(sqlCmd)
            photoFoundRes = dbCur.fetchall()
            
            if (len(photoFoundRes) > 0):
                print('This photo has been downloaded!!!')
                allDownloadedNum += 1
                continue

            photoSizes = flickr_retry_call(flickr, 'photos_getSizes', photo_id=photo.get('id'))
            sizes = photoSizes.getiterator('size')
            
            

            downloadFilename = None
#            if (setting['download_size'].lower() == 'maximum'):
            maximumSize = -1
            for size in sizes:
                imgUrl = size.get('source')
                currentSizeLabel = size.get('label')
                if (currentSizeLabel not in sizeTable):
                    print('Non photo size ' + currentSizeLabel)
                    continue
                currentSizeInt = sizeTable[currentSizeLabel]
                if imgUrl and size.get('label') in sizeTable:
                    if (currentSizeInt > maximumSize 
                        and currentSizeInt <= sizeTable[setting['max_download_size']]):
                        maximumSize = currentSizeInt
            if (maximumSize > 1):
                for size in sizes:
                    imgUrl = size.get('source')
                    currentSizeLabel = size.get('label')
                    if (currentSizeLabel not in sizeTable):
                        print('Non photo size ' + currentSizeLabel)
                        continue
                    currentSizeInt = sizeTable[currentSizeLabel]
                    if imgUrl and currentSizeLabel in sizeTable:
                        if (currentSizeInt == maximumSize):
                            print('Download size ' + currentSizeLabel)
                            urlRes = urllib.parse.urlsplit(imgUrl)
                            (folder, fileName) = os.path.split(urlRes[2])
                            
                            imgFilePath = os.path.join(setting['image_folder'], fileName)
                            try:
                                urllib.request.urlretrieve(imgUrl, imgFilePath)
                                print('Downloaded ' + imgUrl)
                                        
                                downloadFilename = fileName
                            except:
                                print('Network error when downloading the image!')
                                pass

            if (isinstance(downloadFilename, type(None))):
                print('Photo can not be downloaded as either network problem or the absent of specified size!!!')
            else:
                nameComp = downloadFilename.split('_')

                secretStr = None
                sizeStr = ''
                if (len(nameComp) > 2):
                    secretStr = nameComp[1]
                    (tmp, ext) = os.path.splitext(nameComp[2])
                    sizeStr = tmp
                else:
                    (secretStr, ext) = os.path.splitext(nameComp[1])
                    
                ownerStr = photo.get('owner')
                ownerComp = ownerStr.split('@N')
                #ownerInt = int(ownerComp[0])*100 + int(ownerComp[1])
                
                photoTagList = list()
                                
#                photoInfo = flickr.photos_getInfo(photo_id=photo.get('id'))
                photoInfo = flickr_retry_call(flickr, 'photos_getInfo', photo_id=photo.get('id'))
                tags = photoInfo.findall('photo/tags/tag')
                
                if (tags):
                    for tag in tags:
                        photoTagList.append(tag.get('raw'))
                
                photoTagStr = ','.join(photoTagList)
                

                sqlCmd = 'insert into photos values ('\
                + str(photo.get('id')) \
                + ',' + str(int(ownerComp[0])*100 + int(ownerComp[1])) \
                + ',\'' + sizeStr \
                + '\',\'' + secretStr \
                + '\',\'' + photoTagStr.replace('\'', '\'\'') \
                + '\',\'' + photo.get('title').replace('\'', '\'\'') \
                + '\')'
                try:
                    print(sqlCmd)
                except:
                    print('Error when printing to console, but the process is not harmed. Easy!')
                    pass

                dbCur.execute(sqlCmd)
                
          
                newDownloadedNum += 1
                allDownloadedNum += 1
                
                if (newDownloadedNum % setting['db_commit_size'] == 0):
                    dbConnection.commit()
                    print('\n---------------------------------------------')
                    print(str(newDownloadedNum) + ' new images are downloaded!!!')
                    print(str(allDownloadedNum) + ' images are downloaded altogether!!!')
                    print('---------------------------------------------\n')
      
                time.sleep(setting['download_interval'])
        except Exception as e:
            # cleanup code if exception happen
            if (isinstance(e, flickrapi.FlickrError) == True):
                #'Error: %(code)s: %(msg)s'
                errorMsgComp = e.message.split(':')
                if (StripEmptyAtBothEnd(errorMsgComp[2]) == 'Sorry, the Flickr API service is not currently available....'
                    or int(StripEmptyAtBothEnd(errorMsgComp[1])) == 0):
                    time.sleep(60)
                    print('\n-------------------------------')
                    print('Wait 60 seconds before retry!!!')
                    print('-------------------------------\n')
                else:
                    print(str(e))
            if (isinstance(e, http.client.HTTPException) == True):
                print(str(e))
            else:
                raise e
        

    dbConnection.commit()
    dbConnection.close()
    print(str(newDownloadedNum) + ' new images are downloaded!!!')
    print(str(allDownloadedNum) + ' images are downloaded all together!!!')
    return True

def PrintDownloaderHelp():
    (folder, file) = os.path.split(os.sys.argv[0])
    print('=================Command Help=====================')
    print('Usage: ' + file + ' -dw  <setting file = setting.txt> <keyword0> [<more keywords>]')
    pass

def BookletHandler(argVector):
    if (len(argVector) != 2):
        return False
    
    settingPath = argVector[0]
    
    outHtmlPath = argVector[1]
    (htmlFolder, htmlName) = os.path.split(outHtmlPath)
    (htmlName, htmlExt) = os.path.splitext(htmlName)
    imageAbsFolder = os.path.join(htmlFolder, 'thumbnails')
    if (os.path.exists(imageAbsFolder) == False):
        os.mkdir(imageAbsFolder)
    imageRelFolder = os.path.join('.', 'thumbnails')
    
        
    setting = ReadSetting(settingPath, dict())
    
    dataBasePath = setting['db_file']
    
    flickr = flickrapi.FlickrAPI(setting['api_key'])
    
    dbConnection = SetupDatabase(dataBasePath)
    if (isinstance(dbConnection, type(None))):
        return False
    
    dbCur = dbConnection.cursor();
    
    sqlCmd = '''select * from photos'''
    
    dbCur.execute(sqlCmd)
    photoFoundRes = dbCur.fetchall()
     
    with open(outHtmlPath, 'w+b') as htmlFile:
        htmlFile.write(b'<html>\n')
        htmlFile.write(b'<head><title>\n')
        htmlFile.write(str.encode(htmlName+'\n'))
        htmlFile.write(b'</title>\n')
        htmlFile.write( \
b'''<style type="text/css">a {
    border-width: 0px;
    border-style: none;
    font-weight: normal;
    font-style: normal;
    text-decoration: none;
}
</style>\n''')
        htmlFile.write(b'</head>\n');

        htmlFile.write(b'<body>\n')
        
        if (len(photoFoundRes) > 0):
            for result in photoFoundRes:
                try:
                    photoSizes = flickr_retry_call(flickr, 'photos_getSizes', photo_id=result[0])
                except flickrapi.exceptions.FlickrError as err:
                    print(result[0], err)
                    continue
                photoInfo = flickr_retry_call(flickr, 'photos_getInfo', photo_id=result[0])
                photoTitle = photoInfo.findall('photo/title')
                
                sizes = photoSizes.getiterator('size')
                for size in sizes:
                    if (size.get('label') == 'Square'):
                        imgUrl = size.get('source')
                        imageFileAbsPath = os.path.join(imageAbsFolder, str(result[0])+'_s.jpg')
                        try:
                            if (os.path.exists(imageFileAbsPath) == False):
                                urllib.request.urlretrieve(imgUrl, imageFileAbsPath)
                                print('Downloaded ' + imgUrl)
                            else:
                                print(imageFileAbsPath, ' is cached.\n')
                                    
                            imageFileRelPath = os.path.join(imageRelFolder, str(result[0])+'_s.jpg')
                            
                            htmlFile.write(b'<a href="')
                            htmlFile.write(str.encode(size.get('url')))
                            htmlFile.write(b'">\n')
                            htmlFile.write(b'<img src="')
                            htmlFile.write(str.encode(imageFileRelPath))
                            htmlFile.write(b'"')
    
                            htmlFile.write(b' alt="')
    
                            if (photoTitle):
                                if (len(photoTitle) > 0):
                                    htmlFile.write(str.encode(photoTitle[0].text))
                            else:
                                print('No title element')
                            pass
    
                            htmlFile.write(b'"')
                            htmlFile.write(b'></img>\n')
                            htmlFile.write(b'</a>\n')
                            
                        except:
                            print('Network error when downloading the image!')
                            pass
                
                pass
                
        
        htmlFile.write(b'</body>\n')
        htmlFile.write(b'</html>\n')
        
        pass
    
    return True

def PrintBookletHelp():
    (folder, file) = os.path.split(os.sys.argv[0])
    print('=================Command Help=====================')
    print('Usage: ' + file + ' -bl <setting file> <outhtml>')
    pass


def FetchExifHandler(argVector):
    if (len(argVector) != 1):
        print('Incorrect paramters!!!')
        return False
    
    settingPath = argVector[0]
    
    setting = ReadSetting(settingPath, dict())
    
    dataBasePath = setting['db_file']
    
    flickr = flickrapi.FlickrAPI(setting['api_key'])
    
    dbConnection = SetupDatabase(dataBasePath)
    if (isinstance(dbConnection, type(None))):
        return False
    
    dbCur = dbConnection.cursor();
    
    sqlCmd = '''select * from photos'''
    
    dbCur.execute(sqlCmd)
    photoFoundRes = dbCur.fetchall()
    
    fetchedExifNum = 0
    if (len(photoFoundRes) > 0):
        for result in photoFoundRes:
            sqlCmd = '''select * from exifs where id = ''' + str(result[0])
            
            dbCur.execute(sqlCmd)
            exifFoundRes = dbCur.fetchall()
            
            sqlCmd = '''select * from photos where id = ''' + str(result[0])
            
            dbCur.execute(sqlCmd)
            idFoundRes = dbCur.fetchall()
            camMake = ''
            camModel = ''
            focal = -1
            
            if (len(exifFoundRes) > 0):
                print('This exif has been fetched!!!')
#                allDownloadedNum += 1
            else:
                try:
                    exif = flickr_retry_call(flickr, 'photos_getExif', photo_id=result[0])
                    infos = exif.getiterator('exif')
                    for info in infos:
                        if (info.get('label') == 'Model'):
                            rawNode = info.find("raw")
                            if isinstance(rawNode, type(None)) == False:
                                if isinstance(rawNode.text, type(None)) == False:
                                    camModel = rawNode.text
                                    camModel = camModel.strip()
                        if (info.get('label') == 'Make'):
                            rawNode = info.find("raw")
                            if isinstance(rawNode, type(None)) == False:
                                if isinstance(rawNode.text, type(None)) == False:
                                    camMake = rawNode.text
                                    camMake = camMake.strip()
                        if (info.get('label') == 'Focal Length'):
                            focalNode = info.find("raw")
                            focal = '1'
                            if isinstance(focalNode, type(None)) == False:
                                nums = focalNode.text.split()
                                if (len(nums) > 0):
                                    if (-1 == nums[0].find('/')):
                                        focal = float(nums[0])
                                    else:
                                        div = nums[0].split('/')
                                        focal = float(div[0])/float(div[1])
                    if (camModel == '' or focal == -1):
                        print('[No useful information]', result[0])
                        fetchedExifNum += 1
                    else:
                        print(camModel, str(focal))
                except:
                    print('[Exception]', result[0])
                    
                sqlCmd = 'insert into exifs values ('\
                    + str(result[0]) \
                    + ', -1, -1, -1, -1' \
                    + ', ' + str(focal) \
                    + ',\'' + camModel + '\''\
                    + ',\'' + camMake + '\''\
                    + ')'
                    
                try:
                    print(sqlCmd)
                except:
                    camModel = ''
                    camMake = ''
                    sqlCmd = 'insert into exifs values ('\
                        + str(result[0]) \
                        + ', -1, -1, -1, -1' \
                        + ', ' + str(focal) \
                        + ',\'' + camModel + '\''\
                        + ',\'' + camMake + '\''\
                        + ')'
                        
                dbCur.execute(sqlCmd)
                    
                if (fetchedExifNum % setting['db_commit_size'] == 0):
                    dbConnection.commit()
                    print('Commit changes!!!')
                    
                fetchedExifNum += 1
                
            imageName = str(idFoundRes[0][0]) + '_' + str(idFoundRes[0][3])
            if (len(idFoundRes[0][2]) > 0):
                imageName = imageName + '_' + str(idFoundRes[0][2])
            print(imageName)
            exifPath = os.path.join(setting['image_folder'], imageName+'.exif')
            imagePath = os.path.join(setting['image_folder'], imageName+'.jpg')
            if (os.path.exists(imagePath) == False):
                print('[Image not downloaded]', imagePath)
            if (os.path.exists(exifPath) == False):
                with open(exifPath, 'w') as exifFile:
                    exifFile.write('Camera make = ' + camMake + '\n')
                    exifFile.write('Camera model = ' + camModel + '\n')
                    exifFile.write('Focal length = ' + str(focal) + '\n')
                pass

    dbConnection.commit()
    dbConnection.close()
    print(str(fetchedExifNum) + ' exif are fetched!!!')
    
    return True

def PrintFetchExifHelp():
    (folder, file) = os.path.split(os.sys.argv[0])
    print('=================Command Help=====================')
    print('Usage: ' + file + ' -exif <setting file>')
    pass

def DumpCamModelHandler(argVector):
    if (len(argVector) != 2):
        print('Incorrect paramters!!!')
        return False
    
    settingPath = argVector[0]
    modelListPath = argVector[1]
    
    setting = ReadSetting(settingPath, dict())
    
    dataBasePath = setting['db_file']
    
    #flickr = flickrapi.FlickrAPI(setting['api_key'])
    
    dbConnection = SetupDatabase(dataBasePath)
    if (isinstance(dbConnection, type(None))):
        return False
    
    dbCur = dbConnection.cursor();
    
    sqlCmd = '''select * from exifs'''
    
    dbCur.execute(sqlCmd)
    photoFoundRes = dbCur.fetchall()
        
    camModelList = list()
    if (len(photoFoundRes) > 0):
        for result in photoFoundRes:
            camModelList.append(result[7] + ' ' + result[6])
            pass
        
    camModelList.sort()
    
    lastModel = '$$$'
    modelNum = 0
    with open(modelListPath, 'w') as modelFile:
        for model in camModelList:
            if model != lastModel:
                modelFile.write(model+'\n')
                lastModel = model
                modelNum += 1
        pass
    
    dbConnection.close()
    print(str(modelNum) + ' models are written!!!')
    
    return True

def PrintDumpCamModelHelp():
    (folder, file) = os.path.split(os.sys.argv[0])
    print('=================Command Help=====================')
    print('Usage: ' + file + ' -dcam <setting file> <out model list>')
    pass


if __name__ == "__main__":
    print('The number of arguments: %d' % len(os.sys.argv))
    
    cmd = CmdFramework()
    cmd.AddCommand('-DW', DownloaderHandler, PrintDownloaderHelp, 'download images from flickr')
    cmd.AddCommand('-BL', BookletHandler, PrintBookletHelp, 'build booklet from database file')
    cmd.AddCommand('-EXIF', FetchExifHandler, PrintFetchExifHelp, 'build booklet from database file')
    cmd.AddCommand('-DCAM', DumpCamModelHandler, PrintDumpCamModelHelp, 'build booklet from database file')
    cmd.Exec(os.sys.argv)          